﻿using System;

namespace BizControl.Library.Math
{
  class CompareFunctions
  {
    /// <summary>
    /// Tests whether two float values are equal, within an acceptable margin
    /// of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if the values are equal, within <i>epsilon</i>; otherwise, false</returns>
    /// <see cref="float.Equals"/>
    /// <see href="http://stackoverflow.com/questions/485175"/>
    /// <see href="http://www.brainbell.com/tutorials/C++/Comparing_Floating-Point_Numbers_With_Bounded_Accuracy.htm"/>
    public static bool FloatEqualTo(float left, float right, float epsilon)
    {
      return System.Math.Abs(left - right) <= epsilon;
    }

    /// <summary>
    /// Tests whether a float value is greater than another float value, 
    /// within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is greater than <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool FloatGreaterThan(float left, float right, float epsilon)
    {
      // delegate
      return FloatGreaterThan(left, right, epsilon, false);
    }

    /// <summary>
    /// Tests whether a float value is greater than or equal to another float
    /// value, within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is greater than or equal to <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool FloatGreaterThanOrEqualTo(float left, float right, float epsilon)
    {
      // delegate
      return FloatGreaterThan(left, right, epsilon, true);
    }

    private static bool FloatGreaterThan(float left, float right, float epsilon, bool orEqualTo)
    {
      if (FloatEqualTo(left, right, epsilon))
      {
        // within epsilon, so considered equal
        return orEqualTo;
      }
      return left > right;
    }

    /// <summary>
    /// Tests whether a float value is less than another float value,
    /// within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is less than <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool FloatLessThan(float left, float right, float epsilon)
    {
      // delegate
      return FloatLessThan(left, right, epsilon, false);
    }

    /// <summary>
    /// Tests whether a float value is less than or equal to another float value,
    /// within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is less than or equal to<i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool FloatLessThanOrEqualTo(float left, float right, float epsilon)
    {
      // delegate
      return FloatLessThan(left, right, epsilon, true);
    }

    // impl
    private static bool FloatLessThan(float left, float right, float epsilon, bool orEqualTo)
    {
      if (FloatEqualTo(left, right, epsilon))
      {
        // within epsilon, so considered equal
        return orEqualTo;
      }
      return left < right;
    }

    /// <summary>
    /// Tests whether two double values are equal, within an acceptable margin
    /// of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if the values are equal, within <i>epsilon</i>; otherwise, false</returns>
    /// <see cref="double.Equals"/>
    public static bool DoubleEqualTo(double left, double right, double epsilon)
    {
      return System.Math.Abs(left - right) <= epsilon;
    }

    /// <summary>
    /// Tests whether a double value is greater than another double value, 
    /// within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is greater than <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool DoubleGreaterThan(double left, double right, double epsilon)
    {
      // delegate
      return DoubleGreaterThan(left, right, epsilon, false);
    }

    /// <summary>
    /// Tests whether a double value is greater than or equal to another double
    /// value, within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is greater than or equal to <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool DoubleGreaterThanOrEqualTo(double left, double right, double epsilon)
    {
      // delegate
      return DoubleGreaterThan(left, right, epsilon, true);
    }

    // impl
    private static bool DoubleGreaterThan(double left, double right, double epsilon, bool orEqualTo)
    {
      if (DoubleEqualTo(left, right, epsilon))
      {
        // within epsilon, so considered equal
        return orEqualTo;
      }
      return left > right;
    }

    /// <summary>
    /// Tests whether a double value is less than another double value, 
    /// within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is less than <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool DoubleLessThan(double left, double right, double epsilon)
    {
      // delegate
      return DoubleLessThan(left, right, epsilon, false);
    }

    /// <summary>
    /// Tests whether a double value is less than or equal to another double
    /// value, within an acceptable margin of difference.
    /// </summary>
    /// <param name="left">the left side of the comparison</param>
    /// <param name="right">the right side of the comparison</param>
    /// <param name="epsilon">the margin of difference</param>
    /// <returns>true if <i>left</i> is less than or equal to <i>right</i>, within <i>epsilon</i>; otherwise, false</returns>
    public static bool DoubleLessThanOrEqualTo(double left, double right, double epsilon)
    {
      // delegate
      return DoubleLessThan(left, right, epsilon, true);
    }

    private static bool DoubleLessThan(double left, double right, double epsilon, bool orEqualTo)
    {
      if (DoubleEqualTo(left, right, epsilon))
      {
        // within epsilon, so considered equal
        return orEqualTo;
      }
      return left < right;
    }
  }
}
